#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
---------------------------------------------------------------------------
$Id: Twittero $Version: 0.1 - $License: BSD License
$Url: http://code.google.com/p/twittero-client/
$Author: Facundo M. de la Cruz (tty0) - <fmdlc@code4life.com.ar>
$Depends: python v2.6 and python-twitter.
---------------------------------------------------------------------------

Copyright 2010 - Facundo M. de la Cruz(c)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3. Neither the name of copyright holders nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
"""

from __future__ import with_statement 
from optparse import OptionParser
from os import path, chmod, mkdir
# from oauthtwitter.oauthtwitter import OAuthApi
from subprocess import Popen, PIPE
from urllib2 import HTTPError, URLError
from hashlib import sha1
import ConfigParser
import inspect
import stat
import sys
import twitter



##############################################################
__version__ = '0.1'
__author__  = 'Facundo M. de la Cruz <fmdlc@code4life.com.ar>'
__modified__= 'Wed Jan 19 - 2010'
##############################################################



class Interface():
  """ -> This class draw all the console interface for this software"""
  def __init__(self):
    #ANSI Colours
    self.red    = chr(27) + "[0;31m"
    self.redb   = chr(27) + "[1;31m"
    self.green  = chr(27) + "[0;32m"
    self.greenb = chr(27) + "[1;32m"
    self.cyan   = chr(27) + "[0;36m"
    self.white  = chr(27) + "[0;37m"
    self.whiteb = chr(27) + "[1;37m"
  

  def context (self,title,user):
    """ Print interface presentation"""
    print '{0}[{1}Twittero - {2}{3} {4}- User: {5}@{6}{7}{8}]{9}'.format(self.greenb, self.whiteb, self.green, title, self.whiteb,
                                                                         self.green, self.white, user, self.greenb, self.white)
  

  def user(self, screen_name, tweet, largo, user):
    """ Print user messages"""
    screen = "%s@%s%-*s%s:%s %s" %(self.greenb, self.whiteb, largo, screen_name, self.greenb, self.white, tweet)
    user = '@%s' %(user)
    return screen.replace(user,'%s%s%s' %(self.cyan, user, self.white))
  

  def msg(self,title, status, *user):
    """Print information messages and exit"""
    if user:
      print '{0}{1}:{2} @{3}'.format(self.green, title, self.white, user[0])
    else:
      print '{0}{1}{2}'.format(self.green, title, self.white)
    sys.exit(status)


  def status(self, count):
    """ Print status updates"""
    print "{0}Updating your Twitter status - {1}#{2}{3}".format(self.green, self.red, count, self.white) 
    sys.exit(0)


  def error(self, *error):
    """ Print errors messages"""
    if error:
      print "ERROR: {0}.".format(error)
    else:
      print "ERROR: Check your Internet connection, or your account information."
    sys.exit(1)



class OAuthTwittero():
  """-> OAuth class for Twitter Autentication"""
  
  def auth(self):
    """OAuth-Python-Twitter autentication runtimes"""
    CONSUMER_KEY    = 'VFdQ8ABhvwq1myGeJFZg'
    CONSUMER_SECRET = '7461q4rXD108AImH6rKbR7TnRYIyRjQ4NPAYTMrgE'

    auth = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET) 
    request_token = auth.getRequestToken()
    authorization_url = auth.getAuthorizationURL(request_token) 
    
    auth = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET, request_token) 
    access_token = auth.getAccessToken()

    auth = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET, access_token)
    authuser = auth.GetUserInfo(user) 
    


class Twitter(OAuthTwittero):
  """-> Twitter API class"""

  def __init__(self, username, password, timeout, cache, useragent):
    self.api = twitter.Api()
    self.interface = Interface()

    if cache > 0:
      self.api.SetCache(cache)

    if timeout > 0:
      self.api.SetCacheTimeout(timeout)
    
    self.api.SetUserAgent(useragent)
    self.api.SetXTwitterHeaders('Twittero', 'http://code.google.com/p/twittero-client/', '0.1')
    self.api.__init__(input_encoding='utf-8')
    self.api.SetCredentials(username=username, password=password)
    self.api.SetSource('Twit-Tero')
 
  def getFollowing(self,user):
    """Get the following list for you or other Twitter user"""
    try:
      following = self.api.GetFriends(user)
    except(URLError):
      self.interface.error()
    
    return [(f.name, f.screen_name) for f in following]
  

  def getFollowers(self):
    """Get the your followers list"""
    try:
      followers = self.api.GetFollowers()
    except(URLError):
      self.interface.error()

    return [(f.name, f.screen_name) for f in followers]
  

  def Follow(self,user, myuser):
    """Follow Twitter user"""
    if user and user <> myuser: 
      try:
        follow = self.api.CreateFriendship(user)
        return "Following to @%s" %(user)
      except(URLError):
        self.interface.error()
    else:
      self.interface.msg('You cant follow to youserlf', 1)
  

  def Unfollow(self,user,myuser):
    """Unfollow a Twitter user"""
    if user and user <> myuser:
      try:
        unfollow = self.api.DestroyFriendship(user)
        return "Unfollow to @%s" %(user)
      except(URLError):
        self.interface.error()
    else:
      self.interface.msg('You cant unfollow to yourself', 1)
  

  def newTweet(self,status):
    """Send a new tweet"""
    try:
      send = self.api.PostUpdate(status) 
      return int(send.user.statuses_count)
    except(twitter.TwitterError):
      self.interface.msg('Tweet too long', 1)
    except(URLError):
      self.interface.error()
  

  def getPublicTimeLine(self):
    """Get the public Twitter timelie"""
    try:
      statuses = self.api.GetPublicTimeline()
    except(URLError):
      self.interface.error()

    return [(public.user.screen_name, public.text) for public in statuses]
 
  def getTimeLine(self,*user):
    try:
      if user:
        statuses = self.api.GetUserTimeline(user)
        return [s.text for s in statuses]
      else:
        statuses = self.api.GetFriendsTimeline()
        return [(users.user.screen_name, users.text) for users in statuses]
    except:
      self.interface.error()


  def getReplies(self):
    """Print the replies"""
    try:
      replies = self.api.GetReplies()
      return [(r.text, r.user.screen_name) for r in replies] 
    except(URLError):
      self.interface.error()
  

  def getDirectMessage(self):
    """Get the direct messages sending to you"""
    try:
      directmessage = self.api.GetDirectMessages()
      return [(m.sender_screen_name, m.text) for m in directmessage]
    except(URLError):
      self.interface.error()
  

  def postDirectMessage(self, user, status, myuser):
    """Post a direct message"""
    if user <> myuser:
      try:
        self.api.PostDirectMessage(user, status)
      except(URLError):
        self.interface.error()
    else:
      self.interface.msg('No direct messages to yourself', 1)
  

  def playing(self,player):
    """Update your Twitter status with a Musical status - This found only in POSIX"""
    try:
      script = path.expanduser('~/.twittero/scripts/%s.sh' %(player)) # A bug here!
      output  = Popen(script, stdout=PIPE).communicate()[0].split('\n')
      if output[0] == 'noplaying':
        self.interface.msg('Not playing', 1)

      output  = '#NowPlaying: %s' %(output[0])
    except(OSError):
      self.interface.error('Music script not loaded')
    
    return self.newTweet(output)



def readconfig():
  """
    Read the default config file (~/.twittero/twittero.conf) and load it in userdata dictonary
    if this file not exist, this funcion create it.
  """

  # This found only in POSIX
  filename  = path.expanduser('~/.twittero/twittero.conf')
  config    = ConfigParser.ConfigParser()
  interface = Interface()

  try:
    config.read([filename])
    config_username = config.get('account', 'username')
    config_password = config.get('account', 'password')
    
    if config.has_section('options'):
      if config.has_option('options', 'cache') and config.get('options', 'cache') <> '':
        config_cache = config.getint('options', 'cache')
      else:
        config_cache = 0

      if config.has_option('options', 'timeout') and config.get('options', 'timeout') <> '':
        config_timeout = config.getint('options', 'timeout')
      else:
        config_timeout = 10

      if config.has_option('options', 'useragent') and config.get('options', 'useragent') <> '':
        config_useragent= config.get('options', 'useragent')
      else:
        config_useragent = 'Twittero'

      if config.has_option('options', 'player') and config.get('options', 'player') <> '':
        config_player   = config.get('options', 'player')
        config_player   = config_player.lower()
      else:
        config_player = 'none'
    else:
      config_cache     = 0
      config_timeout   = 0
      config_useragent = 'Twittero'
      config_player    = 'none'

    userdata = {'username':config_username, 'password':config_password, 'timeout':config_timeout, 'cache':config_cache, 
                'useragent': config_useragent, 'player':config_player, 'filename':filename}

    return userdata
  
  except(ConfigParser.NoSectionError):
    mkdir(path.expanduser('~/') + '.twittero', 0700)
    mkdir(path.expanduser('~/.twittero/') + 'scripts', 0700)

    config.add_section('account')
    config.add_section('options')
    config.set('account', 'username', '')
    config.set('account', 'password', '')
    config.set('options', 'player', 'none')
    config.set('options', 'cache', '0')
    config.set('options', 'timeout', '60')
    config.set('options', 'useragent', 'Twittero')
    with open(filename, 'wb') as configfile:
      config.write(configfile)
      # Only POSIX - filename: -rw------
      chmod(filename, stat.S_IREAD | stat.S_IWRITE)
      interface.msg("Config file created. Edit it in ~/.twittero/twittero.conf and restart this program.", 1)

  except(ConfigParser.NoOptionError, ConfigParser.NoSectionError):
    interface.msg("Bad config file - Check it in ~/.twittero/twittero.conf and try again.", 1)

  except():
    interface.msg("Error with the ~/.twittero/twittero.conf file, check it.", 1)


def main(userdata):
  """ Main section for this program, the config options, the username and password data, and more """
  
  parser = OptionParser(usage="%prog [ -a | -f | -g | -h | -p | -r | -s | -u | -v | -t] [--replies] [--user] [--follow] [--unfollow] [--following] [--followers] [--public] [status]", version="%prog 0.1")

  parser.add_option("-g", "--message",  action="store_true", dest="mymessage",        help="get direct messages")
  parser.add_option("-s", "--send",     action="store_true", dest="mysendmsg",        help="send a direct message")
  parser.add_option("-u", "--update",   action="store",      dest="myupdate",         help="update Twitter status", metavar="status", type="string")
  parser.add_option("-t", "--timeline", action="store_true", dest="mytimeline",       help="get your timeline")
  parser.add_option("-a", "--account",  action="store",      dest="myusername",       help="set your username", metavar="username", type="string")
  parser.add_option("-p", "--password", action="store",      dest="mypassword",       help="set your password", metavar="password", type="string")
  parser.add_option("-r", "--replies",  action="store_true", dest="myreplies",        help="show replies")
  parser.add_option("--follow",         action="store_true", dest="myfollow",         help="Follow a Twitter user")
  parser.add_option("--unfollow",       action="store_true", dest="myunfollow",       help="Unfollow a Twitter user")
  parser.add_option("--following",      action="store_true", dest="myfollowing",      help="print your following")
  parser.add_option("--followers",      action="store_true", dest="myfollowers",      help="print your followers")  
  parser.add_option("--public",         action="store_true", dest="mypublictimeline", help="Show the public timeline")
  parser.add_option("--user",           action="store",      dest="user",             help="select a user", metavar="username", type="string") 
  parser.add_option("--listening",      action="store_true", dest="mylistening",      help="Show what are you listening",)
  parser.add_option("--player",         action="store",      dest="myplayer",         help="Set audio player", metavar="player", type="string")
  (options, args) = parser.parse_args()

  myupdate           = getattr(options, 'myupdate')
  mytimeline         = getattr(options, 'mytimeline')
  mypublictimeline   = getattr(options, 'mypublictimeline')
  myfollow           = getattr(options, 'myfollow')
  myunfollow         = getattr(options, 'myunfollow')
  myfollowing        = getattr(options, 'myfollowing')
  myfollowers        = getattr(options, 'myfollowers')
  myreplies          = getattr(options, 'myreplies')
  user               = getattr(options, 'user')
  myusername         = getattr(options, 'myusername')
  mypassword         = getattr(options, 'mypassword')
  mymessage          = getattr(options, 'mymessage')
  mysendmsg          = getattr(options, 'mysendmsg')
  mylistening        = getattr(options, 'mylistening')
  myplayer           = getattr(options, 'myplayer')

  if len(sys.argv) < 2:
    parser.error("incorrect number of arguments.")
    sys.exit(1)

  interface = Interface()


  # --------------------------------------------------------------------------------
  # Here i check the account options, how to the username and the password. If 
  # one of this is empty this fail. I try get this data from the -a and -p options.
  # --------------------------------------------------------------------------------
  if myusername and mypassword:
    userdata['username'] = myusername
    userdata['password'] = mypassword

  elif myusername and not mypassword:
    interface.error('You need set your password with the -p option. Or edit the ~/.twittero/twittero.conf file')

  elif mypassword and not myusername:
    interface.error('You need set your username with the -a option. Or edit the ~/.twittero/twittero.conf file')
    sys.exit(1)

  else:
    HASHCONFIG = '8163bb5187a660880707d0c7addea3a70f6639d5' # SHA-1 Checksum ~/.twittero/twittero.conf
      
    f = open(userdata['filename'], 'r')
    config_checksum = sha1(f.read()).hexdigest()
    f.close()
    if HASHCONFIG == config_checksum:
      interface.error("You need set your account settings in ~/.twittero/twittero.conf")
      sys.exit(1)


  union = Twitter(userdata['username'], userdata['password'], userdata['timeout'], userdata['cache'], userdata['useragent'])

  
  if mymessage:
    interface.context('Getting direct messages',userdata['username'])
    get = union.getDirectMessage()
    try:
      largo = max(len(x[0]) for x in get)
      for screen_name, message in get:
        print interface.user(screen_name, message, largo, userdata['username'])
    except(ValueError):
      interface.msg('You not has direct messages', 1)

  elif mysendmsg:
    union.postDirectMessage(user, myupdate, userdata['username'])
    interface.msg('Sending private message to', 1, user)

  elif myfollow:
    mymsg  = union.Follow(user,userdata['username'])
    interface.msg(mymsg, 0)

  elif myunfollow:
    mymsg  = union.Unfollow(user,userdata['username'])
    interface.msg(mymsg, 0)

  elif myfollowing:
    if not user:
      user = userdata['username']

    interface.context('Getting following',user)
    get = union.getFollowing(user)
    try:
      largo = max(len(x[0]) for x in get)
      for name, screen_name in get:
        print interface.user(screen_name, name, largo, user)
    except(ValueError):
      interface.msg("You havent followings", 0)

  elif myfollowers:
    interface.context('Getting followers',userdata['username'])
    get = union.getFollowers()
    try:
      largo = max(len(x[0]) for x in get)
      for name, screen_name in get:
        print interface.user(screen_name, name, largo, user)
    except(ValueError):
      interface.msg("You havent followers", 0)

  elif myupdate and not mysendmsg:
    mymsg = union.newTweet(myupdate)
    interface.status(mymsg)

  elif mylistening:
    if myplayer:
      userdata['player'] = myplayer

    if userdata['player'] and userdata['player'] <> 'none': # Check it
      mymsg = union.playing(userdata['player'])
      interface.status(mymsg)
    else:
      interface.error('No player set - Edit the file ~/.twittero/twittero.conf and set it.')

  elif mytimeline:
    if user:
      interface.context('Getting timeline',user)
      get = union.getTimeLine(user)
      largo = max(len(x[0]) for x in get)
      for statuses in get:
        print interface.user(user, statuses, largo, userdata['username'])
    else:
      interface.context('Getting timeline',userdata['username'])
      get = union.getTimeLine()
      largo = max(len(x[0]) for x in get)
      for screen_name, statuses in get:
        print interface.user(screen_name, statuses, largo, userdata['username'])

  elif mypublictimeline:
    interface.context('Getting public timeline',userdata['username'])
    get = union.getPublicTimeLine()
    largo = max(len(x[0]) for x in get)
    for screen_name, statuses in get:
      print interface.user(screen_name, statuses, largo, userdata['username'])

  elif myreplies:
    interface.context('Getting replies', userdata['username'])
    get = union.getReplies()
    try:
      largo = max(len(x[1]) for x in get)
      for tweet, screen_name in get:
        print interface.user(screen_name, tweet, largo, userdata['username'])
    except(ValueError):
      interface.msg('You havent replies', 0)

if __name__ == '__main__':
  try:
    configuration = readconfig()
    main(configuration)
  except (KeyboardInterrupt):
    sys.exit(0)
